﻿using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;

namespace Corina.Logis
{
    public class LogHelper : IDisposable
    {

        private static LogHelper _instance = null;

        private static readonly object _synObject = new object();

        /// <summary>
        ///单例
        /// </summary>
        public static LogHelper Instance

        {

            get

            {

                if (null == _instance)

                {

                    lock (_synObject)
                    {

                        if (null == _instance)
                        {
                            _instance = new LogHelper();
                        }

                    }

                }
                return _instance;
            }

        }

 

        /// <summary>
        /// 日志对象的缓存队列
        /// </summary>
        private static Queue<LogMessage> _msgs;

 
        /// <summary>
        /// 日志写入线程的控制标记  ture写中|false没有写
        /// </summary>
        private  bool _state;

        private  string _logDirectory = AppDomain.CurrentDomain.BaseDirectory;

        /// <summary>

        /// 日志文件存放目录

        /// </summary>

        public  string LogDirectory

        {

            get { return _logDirectory; }

            set { _logDirectory = value; }

        }

 

        private  LogFileSplit _logFileSplit = LogFileSplit.Daily;

        /// <summary>
        /// 日志拆分类型
        /// </summary>
        public  LogFileSplit logFileSplit
        {
            get { return _logFileSplit; }

            set { _logFileSplit = value; }
        }

 
        private MessageLevel _currentLogLevel = MessageLevel.Error;

        /// <summary>
        /// 当前日志记录等级
        /// </summary>
        public MessageLevel CurrentMsgType

        {

            get { return _currentLogLevel; }

            set { _currentLogLevel = value; }

        }

 

        /// <summary>

        /// 当前负责记录日志文件的名称

        /// </summary>

        private  string _currentFileName="1.log";

 

        private  string _fileNamePrefix = "log_";

 

        /// <summary>

        /// 日志的前缀名称，默认为log_

        /// </summary>

        public string FileNamePrefix

        {

            get { return _fileNamePrefix; }

            set { _fileNamePrefix = value; }

        }

 

        /// <summary>
        /// 日志文件生命周期的时间标记
        /// </summary>
        private  DateTime _CurrentFileTimeSign = new DateTime();

        private  int _maxFileSize = 2;

        /// <summary>
        /// 单个日志文件默认大小(单位：兆)
        /// </summary>
        public  int MaxFileSize
        {
            get { return _maxFileSize; }

            set { _maxFileSize = value; }
        }

 

        /// <summary>
        /// 文件后缀号
        /// </summary>
        private  int _fileSymbol = 0;

        /// <summary>
        /// 当前文件大小(单位：B)
        /// </summary>
        private  long _fileSize = 0;

        /// <summary>
        /// 日志文件写入流对象
        /// </summary>
        private StreamWriter _writer;

        /// <summary>

        /// 创建日志对象的新实例,根据指定的日志文件路径和指定的日志文件创建类型

        /// </summary>

        private LogHelper()

        {

            if (_msgs == null)

            {

                GetCurrentFilename();

                _state = true;

                _msgs = new Queue<LogMessage>();

                Thread thread = new Thread(work);

                thread.Start();

            }

        }

 

        //日志文件写入线程执行的方法

        private void work()

        {

            while (true)

            {

                //判断队列中是否存在待写入的日志

                if (_msgs.Count > 0)

                {

                    LogMessage msg = null;

                    lock (_msgs)

                    {

                        msg = _msgs.Dequeue();

 

                        if (msg != null)

                        {

                            FileWrite(msg);

                        }

                    }

                }

                else

                {

                    //判断是否已经发出终止日志并关闭的消息

                    if (_state)

                    {

                        Thread.Sleep(1);

                    }

                    else

                    {

                        FileClose();

                    }

                }

            }

        }

 

        /// <summary>

        /// 根据日志类型获取日志文件名,并同时创建文件到期的时间标记

        /// 通过判断文件的到期时间标记将决定是否创建新文件。

        /// </summary>

        /// <returns></returns>

        private

            void  GetCurrentFilename()

        {

            DateTime now = DateTime.Now;

            string format = "";

            switch (_logFileSplit)

            {

                case LogFileSplit.Daily:

                    _CurrentFileTimeSign = new DateTime(now.Year, now.Month, now.Day);

                    _CurrentFileTimeSign = _CurrentFileTimeSign.AddDays(1);

                    format = now.ToString("yyyyMMdd'.log'");

                    break;

                case LogFileSplit.Weekly:

                    _CurrentFileTimeSign = new DateTime(now.Year, now.Month, now.Day);

                    _CurrentFileTimeSign = _CurrentFileTimeSign.AddDays(7);

                    format = now.ToString("yyyyMMdd'.log'");

                    break;

                case LogFileSplit.Monthly:

                    _CurrentFileTimeSign = new DateTime(now.Year, now.Month, 1);

                    _CurrentFileTimeSign = _CurrentFileTimeSign.AddMonths(1);

                    format = now.ToString("yyyyMM'.log'");

                    break;

                case LogFileSplit.Annually:

                    _CurrentFileTimeSign = new DateTime(now.Year, 1, 1);

                    _CurrentFileTimeSign = _CurrentFileTimeSign.AddYears(1);

                    format = now.ToString("yyyy'.log'");

                    break;

                default:

                    _fileSymbol++;

                    format = _fileSymbol.ToString() + ".log";

                    break;

            }

            if (File.Exists(Path.Combine(LogDirectory, _currentFileName)))

            {

                _fileSize = new FileInfo(Path.Combine(LogDirectory, _currentFileName)).Length;

            }

            else

            {

                _fileSize = 0;

            }

            _currentFileName=_fileNamePrefix + format.Trim();

        }

 

        //写入日志文本到文件的方法

        private void FileWrite(LogMessage msg)

        {

            try

            {

                if (_writer == null)

                {

                    FileOpen();

                }

 

                if (_writer != null)

                {

                    //判断文件到期标志,如果当前文件到期则关闭当前文件创建新的日志文件

                    if ((_logFileSplit != LogFileSplit.Sizely && DateTime.Now >= _CurrentFileTimeSign)||

                        (_logFileSplit == LogFileSplit.Sizely && ((double)_fileSize / 6) > _maxFileSize))

                    {

                        GetCurrentFilename();

                        FileClose();

                        FileOpen();

                    }

                    _writer.Write(msg.datetime);

                    _writer.Write('\t');

                    _writer.Write(msg.type);

                    _writer.Write('\t');

                    _writer.WriteLine(msg.text);                   

                    _fileSize+=System.Text.Encoding.UTF8.GetBytes(msg.ToString()).Length;  

                    _writer.Flush();

                }

            }

            catch (Exception e)

            {

                Console.Out.Write(e);

            }

        }

 

        //打开文件准备写入

        private void FileOpen()

        {

            _writer = new StreamWriter(LogDirectory + _currentFileName, true, Encoding.UTF8);

        }

 

        //关闭打开的日志文件

        private void FileClose()

        {

            if (_writer != null)

            {

                _writer.Flush();

                _writer.Close();

                _writer.Dispose();

                _writer = null;

            }

        }

 

        /// <summary>

        /// 写入新日志,根据指定的日志对象Msg

        /// </summary>

        /// <param name="msg">日志内容对象</param>

        private void Write(LogMessage msg)

        {

            if (msg.type < CurrentMsgType)

                return;

            if (_msgs != null)

            {

                lock (_msgs)

                {

                    _msgs.Enqueue(msg);

                }

            }

        }

 

        /// <summary>

        /// 写入新日志,根据指定的日志内容和信息类型,采用当前时间为日志时间写入新日志

        /// </summary>

        /// <param name="text">日志内容</param>

        /// <param name="type">信息类型</param>

        public void Write(string text, MessageLevel type)

        {

            Write(new LogMessage(text, type));

        }

 

        /// <summary>
        /// 写入新日志,根据指定的日志内容
        /// </summary>
        /// <param name="text">日志内容</param>
        public void Write(string text)

        {
            Write(text, MessageLevel.Debug);
        }

 

        /// <summary>
        /// 写入新日志,根据指定的日志时间、日志内容和信息类型写入新日志
        /// </summary>
        /// <param name="dt">日志时间</param>
        /// <param name="text">日志内容</param>
        /// <param name="type">信息类型</param>

        public void Write(DateTime dt, string text, MessageLevel type)
        {
            Write(new LogMessage(dt, text, type));
        }

        /// <summary>
        /// 写入新日志,根据指定的异常类和信息类型写入新日志
        /// </summary>
        /// <param name="e">异常对象</param>
        /// <param name="type">信息类型</param>
        public void Write(Exception e)
        {
            Write(new LogMessage(e.Message, MessageLevel.Error));
        }

        public void Write(string sqlString, params SqlParameter[] paras)
        {
            string log = sqlString + "\r\n" + string.Join<SqlParameter>("\r\n", paras);
            Write(new LogMessage(log, MessageLevel.Debug));
        }

        /// <summary>
        /// 销毁日志对象
        /// </summary>
        public void Dispose()
        {
            _state = false;
        }

    }

}
